//
//  Server.swift
//  MockServer
//
//  Created by ethan on 16/10/31.
//  Copyright © 2016年 ethan. All rights reserved.
//

import Foundation
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer


enum Method: String {
    case get = "GET"
    case post = "POST"
    case put = "PUT"
    case delete = "DELETE"
    
    static var all: [String] {
        return [Method.get.rawValue, Method.post.rawValue, Method.put.rawValue, Method.delete.rawValue]
    }
}

enum ContentType: String {
    case json = "application/json"
    case html = "text/html"
    case plain = "text/plain"
    
    static var all: [String] {
        return [ContentType.json.rawValue, ContentType.html.rawValue, ContentType.plain.rawValue]
    }
}


struct Api {
    var address: String
    var contentType: ContentType
    var method: Method
    var body: String
    
    static func defaultApi() -> Api {
        let body = "{\"msg\" : \"helloworld\"}"
        let api = Api(address: "/helloworld", contentType: .json, method: .get, body: body)
        return api
    }
    
    var valid: Bool {
        return !address.isEmpty &&
            !body.isEmpty &&
            validFormat()
    }
    
    func validFormat() -> Bool {
        switch contentType {
        case .json:
            if let data = body.data(using: .utf8) {
                do {
                    let dic = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
                    let valid = JSONSerialization.isValidJSONObject(dic)
                    return valid
                } catch (let error) {
                    print("convert json error: \(error)")
                    return false
                }
            } else {
                return false
            }
            
        default:
            return true
        }
    }
}

struct Config {
    var port: UInt16
    var ip: String
    var contentType: ContentType
    var apiList: [Api]
}

class Server {
    let queue = DispatchQueue.global()
    var server = HTTPServer()
    var running = false
    
    func start(config: Config) {
        queue.sync {
            if (self.running) {
                self.server.stop()
                self.running = false
            }
        }
        // Register your own routes and handlers
    
        server = HTTPServer()
        var routes = Routes()
        for api in config.apiList {
            if (!api.valid) {
                print("api: \(api.address) data not valid")
                break
            }
            
            routes.add(method: HTTPMethod.from(string: api.method.rawValue), uri: api.address, handler: { (request, response) in
                response.setHeader(.contentType, value: api.contentType.rawValue)
                
                response.appendBody(string: api.body)
                response.completed()
            })
        }
        
        // Add the routes to the server.
        server.addRoutes(routes)
        
        server.serverPort = config.port
        
        // Set a document root.
        // This is optional. If you do not want to serve static content then do not set this.
        // Setting the document root will automatically add a static file handler for the route /**
        server.documentRoot = "."
        
        // Gather command line options and further configure the server.
        // Run the server with --help to see the list of supported arguments.
        // Command line arguments will supplant any of the values set above.
        //        configureServer(server)
        
        queue.async {
            do {
                self.running = true
                try self.server.start()
            } catch PerfectError.networkError(let err, let msg) {
                print("Network error thrown: \(err) \(msg)")
                self.running = false
            } catch (let err) {
                self.running = false
            }
        }

    }
    
    func stop() {
        queue.sync {
            self.server.stop()
            self.running = false
        }
    }
}
